// sigslot.h: Signal/Slot classes
// 
// Written by Sarah Thompson (sarah@telergy.com) 2002.
//
// License: Public domain. You are free to use this code however you like, with the proviso that
//          the author takes on no responsibility or liability for any use.
//
// QUICK DOCUMENTATION 
//		
//				(see also the full documentation at http://sigslot.sourceforge.net/)
//
//		#define switches
//			SIGSLOT_PURE_ISO			- Define this to force ISO C++ compliance. This also disables
//										  all of the thread safety support on platforms where it is 
//										  available.
//
//			SIGSLOT_DEFAULT_MT_POLICY	- Where thread support is enabled, this defaults to multi_threaded_global.
//										  Otherwise, the default is single_threaded. #define this yourself to
//										  override the default. In pure ISO mode, anything other than
//										  single_threaded will cause a compiler error.
//
//		PLATFORM NOTES
//
//			Win32						- On Win32, the WIN32 symbol must be #defined. Most mainstream
//										  compilers do this by default, but you may need to define it
//										  yourself if your build environment is less standard. This causes
//										  the Win32 thread support to be compiled in and used automatically.
//
//
//			ISO C++						- If none of the supported platforms are detected, or if
//										  SIGSLOT_PURE_ISO is defined, all multithreading support is turned off,
//										  along with any code that might cause a pure ISO C++ environment to
//										  complain. Before you ask, gcc -ansi -pedantic won't compile this 
//										  library, but gcc -ansi is fine. Pedantic mode seems to throw a lot of
//										  errors that aren't really there. If you feel like investigating this,
//										  please contact the author.
//
//		
//		THREADING MODES
//
//			single_threaded				- Your program is assumed to be single threaded from the point of view
//										  of signal/slot usage (i.e. all objects using signals and slots are
//										  created and destroyed from a single thread). Behaviour if objects are
//										  destroyed concurrently is undefined (i.e. you'll get the occasional
//										  segmentation fault/memory exception).
//
//			multi_threaded_global		- Your program is assumed to be multi threaded. Objects using signals and
//										  slots can be safely created and destroyed from any thread, even when
//										  connections exist. In multi_threaded_global mode, this is achieved by a
//										  single global mutex (actually a critical section on Windows because they
//										  are faster). This option uses less OS resources, but results in more
//										  opportunities for contention, possibly resulting in more context switches
//										  than are strictly necessary.
//
//			multi_threaded_local		- Behaviour in this mode is essentially the same as multi_threaded_global,
//										  except that each signal, and each object that inherits has_slots, all 
//										  have their own mutex/critical section. In practice, this means that
//										  mutex collisions (and hence context switches) only happen if they are
//										  absolutely essential. However, on some platforms, creating a lot of 
//										  mutexes can slow down the whole OS, so use this option with care.
//
//		USING THE LIBRARY
//
//			See the full documentation at http://sigslot.sourceforge.net/
//
//   ****************************************************************************************************************
//   remove thing not about windows
//   only reserve three paramete for CSerialPort : port data data_len
//   ****************************************************************************************************************

#ifndef SIGSLOT_H__
#define SIGSLOT_H__

#include <set>
#include <list>

#if defined(WIN32)
#	define _SIGSLOT_HAS_WIN32_THREADS
#	include <windows.h>
#else
#	define _SIGSLOT_SINGLE_THREADED
#endif

#ifndef SIGSLOT_DEFAULT_MT_POLICY
#	ifdef _SIGSLOT_SINGLE_THREADED
#		define SIGSLOT_DEFAULT_MT_POLICY single_threaded
#	else
#		define SIGSLOT_DEFAULT_MT_POLICY multi_threaded_local
#	endif
#endif

class single_threaded
{
public:
	single_threaded()
	{
		;
	}

	virtual ~single_threaded()
	{
		;
	}

	virtual void lock()
	{
		;
	}

	virtual void unlock()
	{
		;
	}
};

#ifdef _SIGSLOT_HAS_WIN32_THREADS
// The multi threading policies only get compiled in if they are enabled.
class multi_threaded_global
{
public:
	multi_threaded_global()
	{
		static bool isinitialised = false;

		if (!isinitialised)
		{
			InitializeCriticalSection(get_critsec());
			isinitialised = true;
		}
	}

	multi_threaded_global(const multi_threaded_global&)
	{
		;
	}

	virtual ~multi_threaded_global()
	{
		;
	}

	virtual void lock()
	{
		EnterCriticalSection(get_critsec());
	}

	virtual void unlock()
	{
		LeaveCriticalSection(get_critsec());
	}

private:
	CRITICAL_SECTION* get_critsec()
	{
		static CRITICAL_SECTION g_critsec;
		return &g_critsec;
	}
};

class multi_threaded_local
{
public:
	multi_threaded_local()
	{
		InitializeCriticalSection(&m_critsec);
	}

	multi_threaded_local(const multi_threaded_local&)
	{
		InitializeCriticalSection(&m_critsec);
	}

	virtual ~multi_threaded_local()
	{
		DeleteCriticalSection(&m_critsec);
	}

	virtual void lock()
	{
		EnterCriticalSection(&m_critsec);
	}

	virtual void unlock()
	{
		LeaveCriticalSection(&m_critsec);
	}

private:
	CRITICAL_SECTION m_critsec;
};
#endif // _SIGSLOT_HAS_WIN32_THREADS

template<class mt_policy>
class lock_block
{
public:
	mt_policy *m_mutex;

	lock_block(mt_policy *mtx)
		: m_mutex(mtx)
	{
		m_mutex->lock();
	}

	~lock_block()
	{
		m_mutex->unlock();
	}
};

template<class mt_policy>
class has_slots;

template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
class _connection_base3
{
public:
	virtual has_slots<mt_policy>* getdest() const = 0;
	virtual void emit(arg1_type, arg2_type, arg3_type) = 0;
	virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone() = 0;
	virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest) = 0;
};

template<class mt_policy>
class _signal_base : public mt_policy
{
public:
	virtual void slot_disconnect(has_slots<mt_policy>* pslot) = 0;
	virtual void slot_duplicate(const has_slots<mt_policy>* poldslot, has_slots<mt_policy>* pnewslot) = 0;
};

template<class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
class has_slots : public mt_policy
{
private:
	typedef std::set<_signal_base<mt_policy> *> sender_set;
	//Visual Studio 2003
#if _MSC_VER > 1310 
	typedef typename sender_set::const_iterator const_iterator;//vs must has typename
#else
	typedef sender_set::const_iterator const_iterator;
#endif

public:
	has_slots()
	{
		;
	}

	has_slots(const has_slots& hs)
		: mt_policy(hs)
	{
		lock_block<mt_policy> lock(this);
		const_iterator it = hs.m_senders.begin();
		const_iterator itEnd = hs.m_senders.end();

		while (it != itEnd)
		{
			(*it)->slot_duplicate(&hs, this);
			m_senders.insert(*it);
			++it;
		}
	}

	void signal_connect(_signal_base<mt_policy>* sender)
	{
		lock_block<mt_policy> lock(this);
		m_senders.insert(sender);
	}

	void signal_disconnect(_signal_base<mt_policy>* sender)
	{
		lock_block<mt_policy> lock(this);
		m_senders.erase(sender);
	}

	virtual ~has_slots()
	{
		disconnect_all();
	}

	void disconnect_all()
	{
		lock_block<mt_policy> lock(this);
		const_iterator it = m_senders.begin();
		const_iterator itEnd = m_senders.end();

		while (it != itEnd)
		{
			(*it)->slot_disconnect(this);
			++it;
		}

		m_senders.erase(m_senders.begin(), m_senders.end());
	}

private:
	sender_set m_senders;
};

template<class arg1_type, class arg2_type, class arg3_type, class mt_policy>
class _signal_base3 : public _signal_base<mt_policy>
{
public:
	typedef std::list<_connection_base3<arg1_type, arg2_type, arg3_type, mt_policy> *>
		connections_list;

	_signal_base3()
	{
		;
	}

	_signal_base3(const _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
		: _signal_base<mt_policy>(s)
	{
			lock_block<mt_policy> lock(this);
			connections_list::const_iterator it = s.m_connected_slots.begin();
			connections_list::const_iterator itEnd = s.m_connected_slots.end();

			while (it != itEnd)
			{
				(*it)->getdest()->signal_connect(this);
				m_connected_slots.push_back((*it)->clone());

				++it;
			}
		}

	void slot_duplicate(const has_slots<mt_policy>* oldtarget, has_slots<mt_policy>* newtarget)
	{
		lock_block<mt_policy> lock(this);
		connections_list::iterator it = m_connected_slots.begin();
		connections_list::iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			if ((*it)->getdest() == oldtarget)
			{
				m_connected_slots.push_back((*it)->duplicate(newtarget));
			}

			++it;
		}
	}

	~_signal_base3()
	{
		disconnect_all();
	}

	void disconnect_all()
	{
		lock_block<mt_policy> lock(this);
		connections_list::const_iterator it = m_connected_slots.begin();
		connections_list::const_iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			(*it)->getdest()->signal_disconnect(this);
			delete *it;

			++it;
		}

		m_connected_slots.erase(m_connected_slots.begin(), m_connected_slots.end());
	}

	void disconnect(has_slots<mt_policy>* pclass)
	{
		lock_block<mt_policy> lock(this);
		connections_list::iterator it = m_connected_slots.begin();
		connections_list::iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			if ((*it)->getdest() == pclass)
			{
				delete *it;
				m_connected_slots.erase(it);
				pclass->signal_disconnect(this);
				return;
			}

			++it;
		}
	}

	void slot_disconnect(has_slots<mt_policy>* pslot)
	{
		lock_block<mt_policy> lock(this);
		connections_list::iterator it = m_connected_slots.begin();
		connections_list::iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			connections_list::iterator itNext = it;
			++itNext;

			if ((*it)->getdest() == pslot)
			{
				m_connected_slots.erase(it);
				//			delete *it;
			}

			it = itNext;
		}
	}

protected:
	connections_list m_connected_slots;
};

template<class dest_type, class arg1_type, class arg2_type, class arg3_type, class mt_policy>
class _connection3 : public _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>
{
public:
	_connection3()
	{
		pobject = NULL;
		pmemfun = NULL;
	}

	_connection3(dest_type* pobject, void (dest_type::*pmemfun)(arg1_type,
		arg2_type, arg3_type))
	{
		m_pobject = pobject;
		m_pmemfun = pmemfun;
	}

	virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* clone()
	{
		return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>(*this);
	}

	virtual _connection_base3<arg1_type, arg2_type, arg3_type, mt_policy>* duplicate(has_slots<mt_policy>* pnewdest)
	{
		return new _connection3<dest_type, arg1_type, arg2_type, arg3_type, mt_policy>((dest_type *)pnewdest, m_pmemfun);
	}

	virtual void emit(arg1_type a1, arg2_type a2, arg3_type a3)
	{
		(m_pobject->*m_pmemfun)(a1, a2, a3);
	}

	virtual has_slots<mt_policy>* getdest() const
	{
		return m_pobject;
	}

private:
	dest_type* m_pobject;
	void (dest_type::* m_pmemfun)(arg1_type, arg2_type, arg3_type);
};

template<class arg1_type, class arg2_type, class arg3_type, class mt_policy = SIGSLOT_DEFAULT_MT_POLICY>
class signal3 : public _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>
{
public:
	signal3()
	{
		;
	}

	signal3(const signal3<arg1_type, arg2_type, arg3_type, mt_policy>& s)
		: _signal_base3<arg1_type, arg2_type, arg3_type, mt_policy>(s)
	{
			;
		}

	template<class desttype>
	void connect(desttype* pclass, void (desttype::*pmemfun)(arg1_type,
		arg2_type, arg3_type))
	{
		lock_block<mt_policy> lock(this);
		_connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>* conn =
			new _connection3<desttype, arg1_type, arg2_type, arg3_type, mt_policy>(pclass,
			pmemfun);
		m_connected_slots.push_back(conn);
		pclass->signal_connect(this);
	}

	void emit(arg1_type a1, arg2_type a2, arg3_type a3)
	{
		lock_block<mt_policy> lock(this);
		connections_list::const_iterator itNext, it = m_connected_slots.begin();
		connections_list::const_iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			itNext = it;
			++itNext;

			(*it)->emit(a1, a2, a3);

			it = itNext;
		}
	}

	void operator()(arg1_type a1, arg2_type a2, arg3_type a3)
	{
		lock_block<mt_policy> lock(this);
		connections_list::const_iterator itNext, it = m_connected_slots.begin();
		connections_list::const_iterator itEnd = m_connected_slots.end();

		while (it != itEnd)
		{
			itNext = it;
			++itNext;

			(*it)->emit(a1, a2, a3);

			it = itNext;
		}
	}
};

#endif // SIGSLOT_H__

